import { useCallback, useEffect, useRef, ChangeEvent, CSSProperties } from "react";
import { message, RadioChangeEvent } from 'antd';
import { ProtectlandProblem, SelectOptionType, TypeFontFamily, TypeFontSize, TypeProjectSubTable, TableDataCell, TypeWorkBookReadResult } from "../types";
import dayjs from 'dayjs';
import { CellStyleConfig, ColumnWidthConfigMap, DocConfig, DocConfigBaseInfo, DocExtraConfigInfo, DocInstance, ExcelFormatBrush, FileItem, OfficeUnit, SpeciesInfoType, TopicType, TypeOptions } from "./types";
import { CheckboxChangeEvent } from "antd/lib/checkbox";
import { isEqual } from "lodash";
import { DraftInlineStyle } from "draft-js";
import * as XLSX from 'xlsx';
import { DocItem } from "../views/slate-editor";
import redux from './../redux/store';

const toastShort = (type: 'info' | 'warning' | 'error' | 'loading' | 'success', content: string, duration?: number) => {
    if (!duration) {
        duration = 1.5;
    }
    message[type](content, duration);
};

const makeClassNameList = (classNameList: Array<string | any>) => {
    try {
        let classNameStr = '';
        classNameList.forEach(className => {
            if (className) {
                classNameStr = classNameStr + ' ' + className;
            }
        })
        return classNameStr.trim();
    } catch (e) {
        return '';
    }
}

const colorRGB2Hex = (color: string) => {
    var rgb = color.split(',');
    var r = parseInt(rgb[0].split('(')[1]);
    var g = parseInt(rgb[1]);
    var b = parseInt(rgb[2].split(')')[0]);

    var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
    return hex;
}

const hexToRgba = (hex: string, opacity: number) => {
    var RGBA = "rgba(" + parseInt("0x" + hex.slice(1, 3)) + "," + parseInt("0x" + hex.slice(3, 5)) + "," + parseInt("0x" + hex.slice(5, 7)) + "," + opacity + ")";
    return {
        red: parseInt("0x" + hex.slice(1, 3)),
        green: parseInt("0x" + hex.slice(3, 5)),
        blue: parseInt("0x" + hex.slice(5, 7)),
        rgba: RGBA
    }
}

const deepCopy = (value: any) => {
    try {
        return JSON.parse(JSON.stringify(value));
    } catch (e) {
        return ''
    }
};

const adaptFontFamily = (fontFamily: TypeFontFamily) => {
    let fontFamilyCssRule = '';
    switch (fontFamily) {
        case '黑体':
            fontFamilyCssRule = 'SourceHanSansRegular'
            break;
        case '宋体':
            fontFamilyCssRule = 'SourceHanSerif'
            break;
        case '微软雅黑':
            fontFamilyCssRule = 'WeiRuanYaHei'
            break;
        case 'Times New Roman':
            fontFamilyCssRule = 'TimesNewRoman'
            break;
        default:
            break;
    }
    return fontFamilyCssRule;
}

const inlineStyleTemplateList: SelectOptionType[] = [
    {
        value: '宋体',
        label: '宋体',
    },
    {
        value: '微软雅黑',
        label: '微软雅黑',
    },
    {
        value: '黑体',
        label: '黑体',
    },
    {
        value: '仿宋',
        label: '仿宋',
    },
    {
        value: 'Times New Roman',
        label: 'Times New Roman',
    },
]

const fontSizeTemplateList: Array<{
    value: TypeFontSize
    label: TypeFontSize
    px: number
    pt: number
}> = [
        {
            value: '初号',
            label: '初号',
            px: 56,
            pt: 42,
        },
        {
            value: '小初',
            label: '小初',
            px: 48,
            pt: 36,
        },
        {
            value: '一号',
            label: '一号',
            px: 34,
            pt: 26,
        },
        {
            value: '小一',
            label: '小一',
            px: 32,
            pt: 24,
        },
        {
            value: '二号',
            label: '二号',
            px: 29,
            pt: 22,
        },
        {
            value: '小二',
            label: '小二',
            px: 24,
            pt: 18,
        },
        {
            value: '三号',
            label: '三号',
            px: 21,
            pt: 16,
        },
        {
            value: '小三',
            label: '小三',
            px: 20,
            pt: 15,
        },
        {
            value: '四号',
            label: '四号',
            px: 18,
            pt: 14,
        },
        {
            value: '小四',
            label: '小四',
            px: 16,
            pt: 12,
        },
        {
            value: '五号',
            label: '五号',
            px: 14,
            pt: 10.5,
        },
        {
            value: '小五',
            label: '小五',
            px: 12,
            pt: 9,
        }
    ]

const fontSizeList: TypeFontSize[] = ['初号', '小初', '一号', '小一', '二号', '小二', '三号', '小三', '四号', '小四', '五号', '小五'];

const fontFamilyList: TypeFontFamily[] = ['黑体', '宋体', '微软雅黑', 'Times New Roman'];

const adaptFontSize2Px = (fontSize: TypeFontSize) => {
    const findFontSize = fontSizeTemplateList.find(item => {
        return item.label == fontSize
    });
    if (findFontSize) {
        return findFontSize.px;
    };
    return 18;
};

const adaptFontSize2Pt = (fontSize: TypeFontSize) => {
    const findFontSize = fontSizeTemplateList.find(item => {
        return item.label == fontSize
    });
    if (findFontSize) {
        return findFontSize.pt;
    };
    return 12;
}

function isFunction(val: any) {
    return Object.prototype.toString.call(val) === '[object Function]'
}
function isObject(val: any) {
    return Object.prototype.toString.call(val) === '[object Object]'
}
function isArray(val: any) {
    return Object.prototype.toString.call(val) === '[object Array]'
}
function isSet(val: any) {
    return Object.prototype.toString.call(val) === '[object Set]'
}
function isMap(val: any) {
    return Object.prototype.toString.call(val) === '[object Map]'
}
function isSymbol(val: any) {
    return Object.prototype.toString.call(val) === '[object Symbol]'
}
function isDate(val: any) {
    return Object.prototype.toString.call(val) === '[object Date]'
}

function ArrayBuffer(val: any) {
    return Object.prototype.toString.call(val) === '[object ArrayBuffer]'
}

const forEachValue = (obj: any, fn: any) => Object.keys(obj).forEach(key => fn(obj[key], key))

function deepClone(val: any, weakMap = new WeakMap()) {
    try {
        if (isDate(val)) return new Date(+val)
        if (isMap(val)) {
            const map = new Map()
            for (const item of val) map.set(deepClone(item[0], weakMap), deepClone(item[1], weakMap))
            return map
        }
        if (isSet(val)) {
            const set = new Set()
            //@ts-ignore
            val.forEach(item => set.add(deepClone(item), weakMap))
            return set
        }
        if (isSymbol(val)) return Symbol(val.description)
        if (isFunction(val)) {
            if (/^function|^\(\)/.test(val.toString())) {
                return new Function(`return ${val.toString()}`)()
            } else {
                return new Function(`return function ${val.toString()}`)()
            }
        }
        if (!isObject(val)) return val
        const obj = isArray(val) ? [] : {}
        if (weakMap.has(val)) return weakMap.get(val)
        weakMap.set(val, obj)
        //@ts-ignore
        forEachValue(val, (val, key) => obj[key] = deepClone(val, weakMap))
        const symbols = Object.getOwnPropertySymbols(val)
        //@ts-ignore
        forEachValue(symbols, key => obj[Symbol(key.description)] = deepClone(symbols[key], weakMap))
        return obj
    } catch (e) {
        console.log("deep-clone-error", e);
        return val;
    }
}

function _useDebounce(fn: any, delay: any, dep: Array<any> = []) {
    const { current } = useRef({ fn, timer: null });
    useEffect(function () {
        current.fn = fn;
    }, [fn]);
    //@ts-ignore
    return useCallback(function f(...args) {
        if (current.timer) {
            //@ts-ignore
            clearTimeout(current.timer);
        }
        //@ts-ignore
        current.timer = setTimeout(() => {
            //@ts-ignore
            current.fn.call(this, ...args);
        }, delay);
    }, dep)
};

function debounce(fn: Function, ms: number) {
    let timer: any = null;
    //@ts-ignore
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => {
            fn(...args);
        }, ms);
    }
}


function useDebounce(fn: Function, ms: number) {
    const fRef = useRef();
    //@ts-ignore
    fRef.current = fn;
    const result = useCallback(
        //@ts-ignore
        debounce(() => fRef.current(), ms),
        []);
    return result;
}

const getChangeEventValue = (e: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLTextAreaElement> | ChangeEvent<RadioChangeEvent> | RadioChangeEvent): string => {
    try {
        //@ts-ignore
        return e.target.value;
    } catch (err) {
        return "";
    }
}

const getCheckBoxChangeEventValue = (e: CheckboxChangeEvent): boolean => {
    try {
        return e.target.checked;
    } catch (err) {
        return false;
    }
}

const isEmpty = (value: any) => {
    try {
        if (value === null || value === undefined || value === "" || JSON.stringify(value) === '{}') {
            return true
        };
        return false;
    } catch (e) {
        return true;
    }
};

const isNumber = (value: any) => {
    try {
        return !Number.isNaN(Number(value))
    } catch (e) {
        return false;
    }
}

const commonErrorMsg = "操作失败，请稍后重试!";
const commonSuccessMsg = "操作成功!";

const addKeyToList = (list: Array<any>) => {
    let originList = list;
    originList.forEach((ele, index) => {
        ele.key = ele.id ? ele.id : index + '';
        // ele.deletePopConfirmOpened = false;
    })
    return originList;
};

const formatTime = (ns: any) => {
    return dayjs(new Date(ns).getTime()).format('YYYY-MM-DD HH:mm')
}

const formatYear = (ns: any) => {
    return dayjs(new Date(ns).getTime()).format('YYYY')
}

/**
 * list 转 tree 方法1
 * @param originList 
 * @returns 
 */
const generateTreeData = (originList: Array<any>): Array<any> => {
    originList.forEach(ele => {
        ele.children = []
    })
    const [map, treeData] = [{}, []];
    for (let i = 0; i < originList.length; i += 1) {
        map[originList[i].id] = i;
        originList[i].children = [];
    }
    for (let i = 0; i < originList.length; i += 1) {
        const node = originList[i];
        if (node.pid && originList[map[node.pid]]) {
            originList[map[node.pid]].children.push(node);
        } else {
            treeData.push(node);
        }
    }
    return treeData;
};

const addTopicIdForTopicList = (list: TopicType[]) => {
    try {
        return list.map(ele => {
            let obj = {
                topicId: ele.id,
                pid: ele.pid
            }
            return obj;
        })
    } catch (err) {
        return []
    }
}

/**
 * 树转列表
 * @param tree 
 * @returns 
 */
const tree2List = (tree) => {
    const list = []
    const queue = [...tree]
    while (queue.length) {
        const node = queue.shift();
        if (isEmpty(node.children)) {
            node.children = [];
        }
        const children = node.children;
        if (children) {
            queue.push(...children)
        }
        list.push(node)
    }
    // list.forEach(item => {
    //     if(item.children){
    //         delete item.children;
    //     }
    // })
    return list;
}


const addTreePropertyForList = (originList: Array<any>): any[] => {
    let resultList = originList.map(ele => {
        ele.title = ele.name ? ele.name : ele.topicName;
        ele.key = ele.id;
        ele.pid = ele.pid ? ele.pid : 0;
        return ele;
    })
    return resultList;
}

const generateOptionData = (list: any[], labelKey?: string): TypeOptions[] => {
    const options: TypeOptions[] = list.map(ele => {
        return {
            label: ele[labelKey ? labelKey : 'name'],
            value: ele.id
        }
    });
    return options;
}

const defaultAvatar = 'http://shpfile-data-1314977817.cos.ap-guangzhou.myqcloud.com/public/2023032051978eacda-828c-4121-838e-2c0c1439ba6a30.png';


const getUuid = (len: number, radix?: number) => {
    var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
    let uuid = []
    let i
    radix = radix || chars.length
    if (len) {
        for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
    } else {
        let r
        uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
        uuid[14] = '4'
        for (i = 0; i < 36; i++) {
            if (!uuid[i]) {
                r = 0 | Math.random() * 16
                uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]
            }
        }
    }
    // let NS = (new Date().getTime() / 1000).toFixed(0);
    return uuid.join('');
}

const adaptTopicLevel = (level: string): string => {
    let topicLevelText = '';
    switch (level) {
        case '1':
            topicLevelText = '一级';
            break;
        case '2':
            topicLevelText = '二级';
            break;
        case '3':
            topicLevelText = '三级';
            break;
        case '4':
            topicLevelText = '四级';
            break;
        case '5':
            topicLevelText = '五级';
            break;
        default:
            break;
    }
    return topicLevelText;
}

const protectlandProblemList: TypeOptions[] = [
    {
        label: "保护管理方面",
        value: "1",
        children: [
            {
                label: "保护管理建设滞后,保护管理效率有待进一步提高",
                value: "1-1"
            },
            {
                label: "保护与开发矛盾突出",
                value: "1-2"
            },
            {
                label: "管理经费匮乏，基本建设投资有限",
                value: '1-3'
            },
            {
                label: "机构设置不够健全，人员配备不足",
                value: '1-4'
            },
            {
                label: "法律法规和管理制度不够完善",
                value: '1-5'
            },
            {
                label: "保护管理队伍素质有待进一步提高",
                value: '1-6'
            }
        ]
    },
    {
        label: "科研监测方面",
        value: "2",
        children: [
            {
                label: "科研、管理专业人才缺乏",
                value: "2-1"
            },
            {
                label: "科研监测体系建设不健全",
                value: "2-2"
            },
            {
                label: "科研监测领域有待进一步拓宽，重点保护对象的跟踪监测与研究需要持续深入开展",
                value: "2-3"
            },
            {
                label: "科研工作缺乏固定场所，科研水平有待进一步提高",
                value: "2-4"
            },
        ]
    },
    {
        label: "宣传教育方面",
        value: "3",
        children: [
            {
                label: "宣传教育能力还需要提高",
                value: "3-1"
            },
            {
                label: "宣传教育的广度和深度还远远不够",
                value: "3-2"
            }
        ]
    },
    {
        label: "生态旅游方面",
        value: "4",
        children: [
            {
                label: "可持续发展能力需提高",
                value: "4-1"
            }
        ]
    },
    {
        label: "基础设施方面",
        value: "5",
        children: [
            {
                label: "基础设施薄弱、配套设备缺乏",
                value: "5-1"
            },
            {
                label: "管理局站设施急需改善",
                value: "5-2"
            }
        ]
    },
    {
        label: "防灾减灾方面",
        value: "6",
        children: [
            {
                label: "火灾隐患较大",
                value: "6-1"
            },
            {
                label: "防火网络不够健全",
                value: "6-2"
            }
        ]
    },
];

const provinceList: Array<TypeOptions> = [
    {
        value: '北京',
        suffix: '市',
        label: '北京',
    },
    {
        value: '天津',
        suffix: '市',
        label: '天津',
    },
    {
        value: '河北',
        suffix: '市',
        label: '河北',
    },
    {
        value: '山西',
        suffix: '市',
        label: '山西',
    },
    {
        value: '内蒙古',
        suffix: '自治区',
        label: '内蒙古',
    },
    {
        value: '辽宁',
        suffix: '省',
        label: '辽宁',
    },
    {
        value: '吉林',
        suffix: '省',
        label: '吉林',
    },
    {
        value: '黑龙江',
        suffix: '省',
        label: '黑龙江',
    },
    {
        value: '上海',
        suffix: '市',
        label: '上海',
    },
    {
        value: '江苏',
        suffix: '省',
        label: '江苏',
    },
    {
        value: '浙江',
        suffix: '省',
        label: '浙江',
    },
    {
        value: '安徽',
        suffix: '省',
        label: '安徽',
    },
    {
        value: '福建',
        suffix: '省',
        label: '福建',
    },
    {
        value: '江西',
        suffix: '省',
        label: '江西',
    },
    {
        value: '山东',
        suffix: '省',
        label: '山东',
    },
    {
        value: '河南',
        suffix: '省',
        label: '河南',
    },
    {
        value: '湖北',
        suffix: '省',
        label: '湖北',
    },
    {
        value: '湖南',
        suffix: '省',
        label: '湖南',
    },
    {
        value: '广东',
        suffix: '省',
        label: '广东',
    },
    {
        value: '广西',
        suffix: '省',
        label: '广西',
    },
    {
        value: '海南',
        suffix: '省',
        label: '海南',
    },
    {
        value: '重庆',
        suffix: '市',
        label: '重庆',
    },
    {
        value: '四川',
        suffix: '省',
        label: '四川',
    },
    {
        value: '贵州',
        suffix: '省',
        label: '贵州',
    },
    {
        value: '云南',
        suffix: '省',
        label: '云南',
    },
    {
        value: '西藏',
        suffix: '自治区',
        label: '西藏',
    },
    {
        value: '陕西',
        suffix: '省',
        label: '陕西',
    },
    {
        value: '甘肃',
        suffix: '省',
        label: '甘肃',
    },
    {
        value: '青海',
        suffix: '省',
        label: '青海',
    },
    {
        value: '宁夏',
        suffix: '自治区',
        label: '宁夏',
    },
    {
        value: '新疆',
        suffix: '自治区',
        label: '新疆',
    },
    {
        value: '香港',
        suffix: '特别行政区',
        label: '香港',
    },
    {
        value: '澳门',
        suffix: '特别行政区',
        label: '澳门',
    },
    {
        value: '台湾',
        suffix: '省',
        label: '台湾',
    },
];

const protectlandLevelList = [
    { value: '1', label: '国家级' },
    { value: '2', label: '省（自治区、直辖市）级' },
    { value: '3', label: '市（自治州）级' },
    { value: '4', label: '县（自治县、旗、县级市）级' },
]

const isLeafNode = (node: TopicType) => {
    return !node.children || node.children.length === 0;
}

const checkIsDeviceTopic = (node: TopicType) => {
    return node.topicType == 'device';
}


const filterTree = (tree: TopicType[], condition: (node: TopicType) => boolean) => {
    return tree
        .filter((node) => {
            if (node.children) {
                node.children = filterTree(node.children, condition);
            }
            return isLeafNode(node) ? condition(node) : node.children.length > 0;
        })
        .map((node) => ({ ...node }));
}

const filterTreeByDeviceNode = (tree: TopicType[]) => {
    return tree
        .filter((node) => {
            if (node.children) {
                node.children = filterTreeByDeviceNode(node.children);
            }
            return isLeafNode(node) ? checkIsDeviceTopic(node) : node.children.length > 0;
        })
        .map((node) => ({ ...node }));
}


const dfsRecursive = (tree, callback, level = 0) => {
    for (const node of tree) {
        callback(node, level);
        if (node.children) {
            dfsRecursive(node.children, callback, level + 1);
        }
    }
}


const dfsRecursiveFromInner = (tree, callback) => {
    if (!Array.isArray(tree)) return;
    tree.forEach((node) => {
        if (node.children) {
            dfsRecursiveFromInner(node.children, callback);
        }
        callback(node);
    });
}

const parsePrice = (value: any) => {
    let price = Number(Number(value).toFixed(2));
    if (!Number.isNaN(price)) {
        return Number(Number(value).toFixed(2))
    }
    return value;
}

const hasCommonPrefix = (str1: string, str2: string) => {
    const minLength = str1.length;
    if (str2.length <= minLength) {
        return false;
    }
    for (let i = 0; i < minLength; i++) {
        if (str1[i] !== str2[i]) {
            return false;
        }
    }
    return true;
}


const findHalfCheckedKeys = (nodes, checkedKeys) => {
    const halfCheckedKeys = [];
    const traverse = (nodes, parentChecked) => {
        nodes.forEach(node => {
            const isNodeChecked = checkedKeys.includes(node.key);
            const isChildChecked = node.children && node.children.some(child => checkedKeys.includes(child.key));

            if (!isNodeChecked && (parentChecked || isChildChecked)) {
                halfCheckedKeys.push(node.key);
            }

            if (node.children) {
                traverse(node.children, isNodeChecked);
            }
        });
    };
    traverse(nodes, false);
    return halfCheckedKeys;
};


const filterTopicListToNetServe = (topicList: TopicType[]): TopicType[] => {
    let tempTopcList = topicList.map(topic => {
        if (topic.topicType == 'device') {

        } else {
            delete topic.adjustmentCoefficientByYear;
            delete topic.areaConfigList;
            delete topic.defaultRecommand;
            delete topic.children;
            delete topic.count;
            delete topic.createDate;
            delete topic.defaultRecommand;
            delete topic.deviceBackgroundParagraph1;
            delete topic.deviceBackgroundParagraph2;
            delete topic.deviceBackgroundParagraph3;
            delete topic.deviceSortNumber;
            delete topic.deviceType;
            delete topic.facilitiesBackgroundParagraph1;
            delete topic.facilitiesBackgroundParagraph2;
            delete topic.facilitiesBackgroundParagraph3;
            delete topic.facilitiesBackgroundParagraph4;
            delete topic.investmentCompositionMoney;
            delete topic.investmentCompositionType;
            delete topic.isDeleted;
            delete topic.mainProjectRecommand;
            delete topic.mediumAndLongTermMoneyRate
            delete topic.nearFutureMoneyRate
            delete topic.protectlandProblemList
            delete topic.protectlandTypeIdList
            delete topic.provinceConfigList
            delete topic.remark
            delete topic.sort
            delete topic.unit
            delete topic.unitPrice
            delete topic.updateDate
            delete topic.subTopicList
        }
        return topic;
    })
    return tempTopcList;
}

const tableCellDefaultBlockStyle: CSSProperties = {
    justifyContent: 'center',
    alignItems: 'center',
    textAlign: 'center',
    color: '#000000',
    fontSize: 16,
    fontWeight: 400,
}

const comparecomDocInstance = (docInstacne1: DocInstance, comDocInstance2: DocInstance): boolean => {
    try {
        delete docInstacne1.updateComponentName;
        delete comDocInstance2.updateComponentName;
        return isEqual(docInstacne1, comDocInstance2)
    } catch (e) {
        return false;
    }
}


const _findNode = (node: any, targetId: string, result: any) => {
    if (node.id === targetId) {
        result.push(node);
        return;
    }
    if (node.children && node.children.length > 0) {
        for (let i = 0; i < node.children.length; i++) {
            _findNode(node.children[i], targetId, result);
        }
    }
}

const findNodesWithChildren = (tree, targetId) => {
    const result = [];
    _findNode(tree, targetId, result);
    return result;
}

const findTreeNode = () => {

}

const swapListElements = (arr: any[], index1: number, index2: number): any[] => {
    if (index1 !== index2 && index1 >= 0 && index2 >= 0 && index1 < arr.length && index2 < arr.length) {
        [arr[index1], arr[index2]] = [arr[index2], arr[index1]];
    }
    return arr;
}

const findLastTreeNode = (arr: any[], callback: Function) => {
    return arr.reduceRight((prev, cur, index) => {
        if (prev !== undefined) {
            return prev;
        }
        if (callback(cur, index, arr)) {
            return cur;
        }
    }, undefined);
}

const convertStyleMapToObject = (styleMap: Map<string, CSSProperties>) => {
    const styleObject = Array.from(styleMap.entries()).reduce((main, [key, value]) => ({ ...main, [key]: value }), {});
    return styleObject;
};

const copyTextToClipboard = (text) => {
    try {
        text = String(text);
        const inputEL = document.createElement('input');
        inputEL.style.opacity = '0';
        inputEL.style.position = 'absolute';
        inputEL.style.left = '-9999px';
        document.body.appendChild(inputEL);
        inputEL.value = text;
        inputEL.select();
        inputEL.setSelectionRange(0, text.length);
        document.execCommand('copy');
        document.body.removeChild(inputEL);
    } catch (err) {
        toastShort('error', '无法复制到粘贴板，请检查浏览器兼容性！')
    }
}

const isMacOS = (): boolean => {
    const isMac = /macintosh|mac os x/i.test(navigator.userAgent); //苹果
    return isMac;
}

const ctrlKeyName = isMacOS() ? '⌘' : 'Ctrl';

/**
 * 
 * @param cellBound 
 * @param cellConfigList 
 * @returns 
 */
const findExcelStyleByCell = (tableCell: TableDataCell, cellConfigList: CellStyleConfig[], findMode: 'cellId' | 'cellBound') => {
    try {
        const tableCellBoundOrId = findMode == 'cellId' ? tableCell.id + '-' + tableCell.bound[0] : tableCell.bound.toString();
        const findCellConfig = cellConfigList.find(item => {
            const cellBoundOrId = findMode == 'cellId' ? item.cellId : item.colIndex + ',' + item.rowIndex;
            return cellBoundOrId == tableCellBoundOrId;
        })
        return findCellConfig;
    } catch (e) {
        return null;
    }
}


const mergeCellConfigList = (
    tableCellList: TableDataCell[],
    newCellConfig: CellStyleConfig,
    cellConfigList: CellStyleConfig[],
    mergeMode: 'cellId' | 'cellBound'
) => {
    try {
        // console.log("数据---->", tableCellList, newCellConfig, cellConfigList)
        delete newCellConfig.cellId;
        delete newCellConfig.colIndex;
        delete newCellConfig.rowIndex;
        tableCellList.forEach(tableCell => {
            const tableCellId = `${tableCell.id}-${tableCell.bound[0]}`;
            // console.log("tableCellId--->", tableCellId)
            let findIndex = -1;
            for (let i = 0; i < cellConfigList.length; i++) {
                const cellTarget = cellConfigList[i].cellId;
                if (cellTarget == tableCellId) {
                    findIndex = i;
                    break;
                }
            }
            if (findIndex != -1) {
                cellConfigList[findIndex] = {
                    ...cellConfigList[findIndex],
                    ...newCellConfig
                }
            } else {

                let _newCellConfig: CellStyleConfig = {
                    ... {
                        type: 'cell',
                        cellId: tableCellId,
                        colIndex: tableCell.bound[0],
                        rowIndex: tableCell.bound[1],
                    },
                    ...newCellConfig
                }
                cellConfigList.push(_newCellConfig)
            }
        })
        // console.log("merge之后的", cellConfigList)
        return cellConfigList
    } catch (e) {
        return cellConfigList;
    }
}

const convertCellStyleConfig = (type: 'inline' | 'block', cellStyleConfig: CellStyleConfig): CSSProperties => {
    let styleObj: CSSProperties = {};
    if (isEmpty(cellStyleConfig)) {
        return styleObj;
    }
    if (type == 'inline') {
        //粗体
        if (!isEmpty(cellStyleConfig.inlineFontBold) && cellStyleConfig.inlineFontBold) {
            styleObj.fontWeight = 'bold'
        } else {
            styleObj.fontWeight = 'normal'
        }
        //颜色
        if (!isEmpty(cellStyleConfig.inlineFontColor)) {
            styleObj.color = cellStyleConfig.inlineFontColor;
        } else {
            styleObj.color = '#000000'
        }
        //删除线
        if (!isEmpty(cellStyleConfig.inlineFontDeleteLine) && cellStyleConfig.inlineFontDeleteLine) {
            styleObj.textDecoration = 'line-through';
        }
        //下划线
        if (!isEmpty(cellStyleConfig.inlineFontUnderLine) && cellStyleConfig.inlineFontUnderLine) {
            styleObj.textDecoration += ' underline';
        }
        //字体
        if (!isEmpty(cellStyleConfig.inlineFontFamily)) {
            const fontFamily = adaptFontFamily(cellStyleConfig.inlineFontFamily);
            styleObj.fontFamily = fontFamily;
        } else {
            styleObj.fontFamily = 'unset';
        }
        //字号
        if (!isEmpty(cellStyleConfig.inlineFontSize)) {
            const fontSize = adaptFontSize2Pt(cellStyleConfig.inlineFontSize);
            styleObj.fontSize = `${fontSize}pt`;
        } else {
            styleObj.fontSize = '16px';
        }
        //斜体
        if (!isEmpty(cellStyleConfig.inlineFontItalic) && cellStyleConfig.inlineFontItalic) {
            styleObj.fontStyle = 'italic';
        } else {
            styleObj.fontStyle = 'normal';
        }
        if (!isEmpty(cellStyleConfig.inlineFontSuperScript)) {
            // styleObj.fontSize = '12px!important'

            if (cellStyleConfig.inlineFontSuperScript == 'super') {
                styleObj.verticalAlign = 'super';
                styleObj.transform = 'scale(0.72) translateY(-3px)';
            } else {
                styleObj.verticalAlign = 'sub';
                styleObj.transform = 'scale(0.72) translateY(3px)';
            }
        } else {
            styleObj.verticalAlign = 'unset'
        }
        if (!isEmpty(cellStyleConfig.inlineBackgroundColor)) {
            styleObj.backgroundColor = cellStyleConfig.inlineBackgroundColor;
        } else {
            styleObj.backgroundColor = 'transparent'
        }
        // if (!isEmpty(cellStyleConfig.cellAlign)) {
        //     //@ts-ignore
        //     styleObj.textAlign = cellStyleConfig.cellAlign;
        // }
    } else {
        // if (!isEmpty(cellStyleConfig.cellAlign)) {
        //     switch(cellStyleConfig.cellAlign){
        //         case 'left':
        //             break;
        //         case 'center':
        //             break;
        //         case 'right':
        //             break;
        //         case 'justify':
        //             break;
        //         case 'space':
        //             break;
        //     }   
        // } else {
        //     styleObj.justifyContent = 'center';
        //     styleObj.alignItems  = 'center'
        // }
    }
    return styleObj;
}

const defaultExcelCellStyleConfig: CellStyleConfig = {
    inlineFontFamily: '宋体',
    inlineFontSize: '小四',
    inlineFontBold: false,
    inlineFontColor: '#000000',
    inlineFontItalic: false,
    inlineFontUnderLine: false,
    inlineFontSuperScript: null,
    cellHorizontalAlign: 'center',
    cellVerticalAlign: 'center',
    inlineBackgroundColor: '',
    cellTextWrapMode: '0'
}

const defaultDocConfig: DocConfig = {
    xAxisConfigList: [],
    // xAxisConfigList1: [],
    yAxisConfigList: [],
    cellConfigList: [],
    docBaseConfigInfo: {
        topicTreeLinkage: true,             //大纲联动
        showTopicTreeIcon: false,           //大纲图标
        topicSideBarLocked: false,          //大纲锁定
        showTopicTreeLevel: true,           //展示当前大纲的序号
        systemRecommandNodeDeleted: false,  //是否已删除系统推荐的大纲
        docMoneyCardinalNumber: 1,
    }
}

const getObjectWithIntersectionAttributes = (objects) => {
    const attributeSets = objects.map(obj => new Set(Object.keys(obj)));
    const intersection = [...attributeSets[0]].filter(attribute => {
        return attributeSets.every(set => set.has(attribute));
    });
    const result = {};
    intersection.forEach(attribute => {
        result[attribute] = objects[0][attribute];
    });
    return result;
}

const deleteObjectEmptyKey = (obj: Object) => {
    for (let key in obj) {
        if (isEmpty(obj[key])) {
            delete obj[key]
        }
    }
    return obj;
}

const adaptDocMoneyCardinalNumberName = (comDocConfig: DocConfig): string => {
    try {
        if (comDocConfig && comDocConfig.docBaseConfigInfo && comDocConfig.docBaseConfigInfo.docMoneyCardinalNumber) {
            let docMoneyCardinalNumberName = '';
            const docMoneyCardinalNumber = comDocConfig.docBaseConfigInfo.docMoneyCardinalNumber;
            switch (docMoneyCardinalNumber) {
                case 1:
                    docMoneyCardinalNumberName = '万元'
                    break;
                case 10:
                    docMoneyCardinalNumberName = '千元'
                    break;
                case 10000:
                    docMoneyCardinalNumberName = '元'
                    break;
                default:
                    break;
            }
            return docMoneyCardinalNumberName;
        }
        return '未知'
    } catch (e) {
        return '未知'
    }
}



const handleDownloadByName = (url: string, downLoadFileName: string, openNewWindow?: boolean) => {
    const link = document.createElement('a')
    link.style.display = 'none';
    link.href = url
    link.setAttribute(
        'download',
        downLoadFileName ? downLoadFileName : "未命名文件"
    )
    // console.log("link---->", link)
    if (openNewWindow) {
        link.target = "_new";
    }
    document.body.appendChild(link)
    link.click();
    document.body.removeChild(link);
};

const handleDownloadByNameV2 = (url: string, filename: string) => {
    fetch(url).then(response => response.blob()).then(blob => {
        // 创建一个blob链接
        const blobUrl = URL.createObjectURL(blob);
        // 创建a标签
        const link = document.createElement('a');
        link.href = blobUrl;
        link.download = filename;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        // 释放blob链接
        URL.revokeObjectURL(blobUrl);
    }).catch(e => console.error('Download failed:', e));
}

const downloadAndRenameFile = (url: string, newName: string) => {
    fetch(url)
        .then(response => response.blob()) // 将响应转换为Blob
        .then(blob => {
            // 创建一个链接元素
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = newName; // 设置下载的文件名
            // 触发下载
            document.body.appendChild(link);
            link.click();
            // 清理
            document.body.removeChild(link);
            URL.revokeObjectURL(link.href);
        })
        .catch(e => console.error('下载错误:', e));
}


const getTextWidth = (text: any, font: string) => {
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');

    // 设置字体样式
    ctx.font = font;

    // 获取字体的fontsize
    var fontSize = parseInt(ctx.font);

    // 计算文本的宽度
    var width = ctx.measureText(text).width;

    // 返回结果
    return {
        width: width,
        fontSize: fontSize
    };
}

const checkeTwoSlateParagraphListEquals = (docItemList1: DocItem[], docItemList2: DocItem[]) => {
    try {
        let isEquals = true;
        if (docItemList1 && docItemList1.length >= 0 && isEmpty(docItemList2)) {
            return false;
        } else if (docItemList2 && docItemList2.length >= 0 && isEmpty(docItemList1)) {
            return false;
        }
        if (
            (isEmpty(docItemList1) && isEmpty(docItemList2)) ||
            (docItemList1.length == 0 && docItemList2.length == 0)
        ) {
            return true;
        } else if (docItemList1 && docItemList2 && docItemList1.length !== docItemList2.length) {
            return false;
        }
        docItemList1.forEach((docItem1, index) => {
            let docItem2 = docItemList2[index];
            if (isEmpty(docItem2) ||
                JSON.stringify(docItem1) !== JSON.stringify(docItem2) ||
                docItem1.text !== docItem1.text ||
                (docItem1.data && JSON.stringify(docItem1.data) !== JSON.stringify(docItem2.data))
            ) {
                isEquals = false;
            }
        })
        console.log("checkeTwoSlateParagraphListEquals--->", isEquals)
        return isEquals;
    } catch (e) {
        console.log("checkeTwoSlateParagraphListEquals--->err", e, docItemList1, docItemList2)
    }
}

const checkTwoDocInstanceEquals = (comDocInstance1: DocInstance, comDocInstance2: DocInstance): boolean => {
    try {
        if (isEmpty(comDocInstance1) || isEmpty(comDocInstance2)) {
            return false;
        }
        let topicList1 = comDocInstance1.topicList;
        let topicList2 = comDocInstance2.topicList;
        if (topicList1.length !== topicList2.length) {
            return false;
        }
        topicList1 = topicList1.map(topic => {
            delete topic.children;
            return topic;
        })
        topicList2 = topicList2.map(topic => {
            delete topic.children;
            return topic;
        })
        let isEquals = true;
        topicList1.forEach((topic1, index) => {
            let topic2 = topicList2[index];
            if (isEmpty(topic2)) {
                isEquals = false;
            } else if (JSON.stringify(topic1.topicName) !== JSON.stringify(topic2.topicName)) {
                isEquals = false;
            } else if (JSON.stringify(topic1.unit) !== JSON.stringify(topic2.unit)) {
                isEquals = false;
            } else if (JSON.stringify(topic1.count) !== JSON.stringify(topic2.count)) {
                isEquals = false;
            } else if (!checkeTwoSlateParagraphListEquals(topic1.topicSlateDocItemList, topic2.topicSlateDocItemList)) {
                isEquals = false;
            }
        })
        return isEquals;
    } catch (e) {
        return false;
    }
}

const getCurrentDateNs = (size?: 10 | 13): number => {
    if (!size) {
        size = 13;
    }
    //@ts-ignore
    let timeNs = size == 10 ? parseInt(new Date().getTime() / 1000) : parseInt(new Date().getTime());
    return timeNs
}

const getSingleTdTableHeight = (height: number): number => {
    let tableContainerHeight = height && height > 50 ? height - 47 : 0;
    return tableContainerHeight;
}


const insertArrayAtPosition = (arr1: any[], arr2: any[], index: number) => {
    if (index > arr1.length) {
        return arr1.concat(arr2)
    }
    // 在指定位置插入arr2到arr1中
    arr1.splice(index, 0, ...arr2);
    return arr1;
}

const isNonNegativeNumeric = (str) => {
    return /^\d+(\.\d+)?$/.test(str);
};

const getFileType = (file_path: string) => {
    try {
        let index = file_path.lastIndexOf(".");
        return '.' + file_path.substr(index + 1);
    } catch (e) {
        return ''
    }
};

const getFileTypeName = (item: FileItem) => {
    try {
        if (item.type == 1) {
            return '文件夹';
        }
        const fileType = getFileType(item.path);
        switch (fileType.toLowerCase()) {
            case '.png':
            case '.jpg':
            case '.jpeg':
                return fileType.substring(1).toUpperCase() + '图像';
                break;
            case '.doc':
            case '.docx':
                return fileType.substring(1).toUpperCase() + '文稿';
                break;
            case '.xlx':
            case '.xlsx':
                return fileType.substring(1).toUpperCase() + '表格';
                break;
            case '.ppt':
            case '.pptx':
                return fileType.substring(1).toUpperCase() + '演示';
                break;
            case '.zip':
            case '.rar':
                return '文件压缩包';
                break;
            default:
                return fileType.substring(1).toUpperCase() + '文件';
                break;
        }
    } catch (e) {
        return ''
    }
}

const getFileSize = (item: FileItem) => {
    if (item.type == 1) {
        return '--'
    }
    const fileSize = Number(item.size);
    if (fileSize < 1) {
        return (fileSize * 1024).toFixed(2) + 'KB'
    } else {
        return (fileSize).toFixed(2) + 'MB'
    }
}

const findFolderPath = (data, id) => {
    const findById = (data, id) => {
        for (let i = 0; i < data.length; i++) {
            if (data[i].id === id) {
                return data[i];
            }
        }
        return null;
    };
    let path = [];
    let current = findById(data, id);

    while (current != null) {
        path.unshift(current);
        current = findById(data, current.parentFolderId);
    }

    return path;
}


function textWidth(text: string, fontSize: string, fontFamily: string) {
    const el = document.createElement('span');
    el.style.display = 'inline-block';
    el.style.position = 'absolute';
    el.style.zIndex = '-900';
    el.style.whiteSpace = 'nowrap';
    el.style.fontSize = fontSize;
    el.style.fontFamily = fontFamily;
    el.textContent = text;
    document.body.appendChild(el);
    const width = el.clientWidth;
    document.body.removeChild(el);
    return width;
}



const replaceUnitUpperCase = (str: string) => {
    try {
        const regex = /(m2|m3)/g;
        let resultStr = str.replace(regex, (matched) => {
            return matched === "m2" ? "㎡" : "m³";
        })
        return resultStr;
    } catch (e) {
        return str;
    }
}

const protectlandProblemTableData: ProtectlandProblem[] = [
    {
        key: '1',
        value: "1-1",
        problemType: '保护管理方面',
        problemDetails: '保护管理建设滞后,保护管理效率有待进一步提高',
        checked: false,
        backgroundParagraph1: `[*]目前巡护主要是依靠步行和摩托车相结合，巡护用车缺乏。由于区内大部分区域山高路崎岖，大部分地段还只能靠步行进行巡护，不仅耗费较多的人力、时间，而且效率低，影响[*]整体建设和管理水平的提高。这种原始式的保护管理已难以适应当前信息社会建设完备的生态体系和发达产业体系对林业现代化、高效建设的形势要求。
        特别是在发生突发事件或紧急出动处置破坏自然资源案件时，步行或骑摩托车，速度慢、人员安全隐患大，租车费时和临时聘请的驾驶人员情况不熟、配合协调性差，并存在走漏消息的风险。
        [*]应该进一步建设和完善基础设施，提高[*]的管理效率，包括维修[*]的巡护和防火道路，改善巡护等交通工具，配备相关设备。`
    },
    {
        key: '2',
        value: "1-2",
        problemType: '保护管理方面',
        problemDetails: '保护与开发矛盾突出',
        checked: false,
        backgroundParagraph1: `[*]经济来源有限，为增加收入，近几年来[*]兴起了林副产品开发项目，有些项目由于缺乏统一的规划或安排，对[*]的资源保护造成了一定影响。为了达到自然资源的合理持续利用，要求资源开发有一定程度的限制，开发建设规模需要严加控制。[*]周边地区经济欠发达，在一定程度上存在侵占、毁林、开垦等活动。设法协调自然保护与资源开发的关系，对[*]发展非常重要。`,
        backgroundParagraph2: `随着社会的不断发展，当地群众对资源开发和利用的需求不断加大，特别是社区经济单一，群众对本土资源利用的过度依赖， 要求开发生态旅游和休闲旅游的迫切愿望，增加了植被破坏和环境污染的程度。非木质林产品采集、滥伐和盗伐等极个别现象的存在对[*]的资源保存威胁越来越大。人们对[*]的认识还有待进一步提高，实验区内人为活动较为频繁，不仅管理效率低，而且管护盲区大，增加了管护难度。
        尽管[*]管理局已经与社区就自然[*]内土地权属问题达成协议，自然[*]与社区在土地权属方面目前并不存在明显冲突。然而，在当前南方集体林区林权改革的大背景下，随着林地经济价值的日益突出，该[*]与社区在土地权属方面还是存在一定的隐患。
        为了解决自然保护和社区经济发展的矛盾，一方面应该加大保护和宣传力度，让社区居民进一步了解和认识保护的重要性和意义；另一方面，[*]应该与地方政府，共同发展社区经济，增加投入，改变社区居民传统的生产生活方式，提供可持续发展的增产增收技术。`
    },
    {
        key: '3',
        value: "1-3",
        problemType: '保护管理方面',
        problemDetails: '管理经费匮乏，基本建设投资有限',
        checked: false,
        backgroundParagraph1: `经费不足一直是困扰[*]发展的主要原因。[NAME]保护经费一直未列入[P]财政预算，[*]日常的管理、宣教等费用主要靠林业事业经费来维持，不能满足[*]正常运转需要，大大限制了[*]的发展。`
    },
    {
        key: '4',
        value: "1-4",
        problemType: '保护管理方面',
        problemDetails: '机构设置不够健全，人员配备不足',
        checked: false,
        backgroundParagraph1: `[*]目前由乡镇林业站管理，没有单独设立[*]管理站，人员配备不足，管理能力有限，管理力度不够。随着[*]的建设与发展，组织机构不完善和工作人员不足的矛盾将更加突出，因此，应加强组织机构建设并增加科技、保护人员。`,
        backgroundParagraph2: `[*]巡护监测员主要来源于林场职工、社会招聘和社区群众，多数未经过系统培训、考核和定级，工作人员普遍存在知识更新缓慢、知识结构不全等问题；[*]现有专业技术人员结构不平衡，其中林学、林管等方面技术力量较强，缺乏植物分类、生态学、动物学、森林保护学和森林病理学等方面的人才；由于缺乏科研经费，现有技术人员接受专业培训的机会较少，知识拓展机会少，业务水平提高缓慢，难以开展专题性的科研、考察和资源监测工作；[*]地处偏远山区，生活条件艰苦，很难吸引高素质人才。
        [*]应该通过举办或者参加国内外有关自然保护管理的学术交流、讲座等活动，对[*]技术人员进行继续教育，提高管理人员能力培训等方式，增加[*]管理人员的业务素质。同时，应该进一步创新和改善[*]的工作环境和条件，吸引更多的人才加入到[*]的工作中来。`
    },
    {
        key: '5',
        value: "1-5",
        problemType: '保护管理方面',
        problemDetails: '法律法规和管理制度不够完善',
        checked: false,
        backgroundParagraph1: `[*]虽然已建立了一些内部管理制度，但还需要根据自然[*]的有关法律、法规和[*]状况进一步修改和完善。
        [*]管理机构属于事业单位，而《自然[*]条例》赋予[*]管理机构的职责涉及：林业、环境、农业、国土等行业。依据目前的委托林业行政执法，难以履行自然[*]管理机构的职责。
        因此，应该进一步完善[*]的法律法规和管理制度的建设，进一步规范野外巡护和科研监测制度等，以便明确[*]的管理权限、职能、责任和执法程序。`
    },
    {
        key: '6',
        value: "1-6",
        problemType: '保护管理方面',
        problemDetails: '保护地信息化、数字化建设有待完善',
        checked: false,
        backgroundParagraph1: `[*]应该加强数字化管理的建设，数字化管理是实现科学管理、科学决策、应急高效的基础性前提，也是自然[*]发展建设的方向。通过将物种编目、巡护执法动态、监控监测网络、科研监测成果、社区生产活动和经济发展、管理机构和区划规划调整等全因子数字化，实现远程、实时、实景、动态、互动式管理。`
    },
    {
        key: '7',
        value: "1-7",
        problemType: '保护管理方面',
        problemDetails: '保护地内历史遗留问题多，空间范围亟待优化调整',
        checked: false,
        backgroundParagraph1: `[*]建立之前，区内及周边社区居民就在此居住史长久，保留着传统的生产生活方式，导致了养殖场、农业用地、居民点、产业 转型等众多历史遗留问题。尽管近年来通过保护执法和社区宣传教育活动，大大改观了周边群众的自然保护观念，但是仍无法从根本上制止他们，容易导致与当地居民关系紧张。这些历史遗留问题一直困扰着[*]，也是阻碍[*]有效管理的制约瓶颈。结合自然 资源部和国家林草局部署的自然保护地整合优化工作，[*]在本底调查的基础上，开展自然生态和人文价值评估、[*]现状与管护成效评估，科学分析当前[*]质量状况、空间分布和建设管理现状，统筹考虑自然生态系统的完整性和经济社会发展的需要，科学评估[*]生态价值、功能地位、保护效果、发展空间和存在问题、对周边经济社会发展影响等，形成了[*]范围和功能区优化调整的初步方案。此次总体规划，综合考虑了[*]主要保护对象分布、现有管护设施设备、社区居民活动以及将来调整优化情况，在仍保留的严格[*]域重点规划布局生态保护修复和科研监测等项目，仍保留的一般[*]域主要规划公众教育等项目，拟调出区域一并加强可持续发展项目规划。`,
    },
    {
        key: '8',
        value: "1-8",
        problemType: '保护管理方面',
        problemDetails: '保护地管护制度建设薄弱',
        checked: false,
        backgroundParagraph1: `目前[*]管护站点和巡护体系建设，仅能满足基本管护需求，应增强管护能力。自然 [*]也需要增加管护人员，加大巡护力度，尽早修订并落实《监管站驻站制度》， 实行监管站24小时驻站值班制度；制定《公益性岗位职责》，坚持严格巡护执 法；严格落实“首查负责制”。`,
    },
    {
        key: '9',
        value: "2-1",
        problemType: '科研监测方面',
        problemDetails: '科研、管理专业人才缺乏',
        checked: false,
        backgroundParagraph1: `[*]自成立以来科研、管理专业人才缺乏，不利于科学研究和管理水平的提高。需要引入高知识、高层次人才，加强[*]工作人员培训，常年定期举办各种业务培训班，提高管理人员素质和工作质量，建设一支训练有素、精通业务、善于管理的队伍。`,
        backgroundParagraph2: `[*]点多面广线长，人员编制偏紧，且现有人员专业结构也不能满足自然保护工作的需要，专业知识培训十分必要；有相当部分人员，尤其是科技人员需培训提高。`,
        backgroundParagraph3: `[*]在过去开展的一系列科研监测工作，主要是围绕脊椎动物、高等植物开展，对于其他类别的物种和生态、景观方面的研究很少，特别是对于生物资源赖以生存的土壤、水文、地质等无生命世界的研究和生态服务功能基本还是空白，而这些方面的研究又是事关生物生存、繁衍、演替的重要基础条件和体现自然[*]价值。同时，经过长期的调查、研究、监测，一些在国内、乃至世界生物多样性保护方面都具有不可替代地位的重要资源，需要进行长期的跟踪监测、进行深入的分析研究，使这些宝贵的珍稀濒危物种得到更为有效的保护。
        [*]应该增加科研监测内容。并且通过培训交流等方式，培养科研监测队伍，提供科研人员素质，增强科研队伍整体实力。`
    },
    {
        key: '10',
        value: "2-2",
        problemType: '科研监测方面',
        problemDetails: '科研监测体系建设不健全',
        checked: false,
        backgroundParagraph1: `目前，[*]仅开展森林资源的定期监测、人为活动监测，此外多为小规模的补充专项调查。仅有资源静态监测，缺乏自然资源和自然环境的动态变化监测，缺乏生物多样性监测。现有的数据收集格式也不完整，无法集中分析。没有数据管理体系，无法用数据直观地体现出[*]的保护成效。`,
        backgroundParagraph2: `[NAME]科研监测体系不健全，体现在：（1）科研监测基础设施严重不足，科研监测的仪器缺乏或者陈旧，监测点、监测样地与样带尚未完善；（2）[*]没有数据库管理系统，例如缺少野生植物资源、动物资源等数据库系统；缺少将这些资源数据库系统与地理信息系统整合在一起形成矢量化资源数据库系统，资源数据管理效率低；（3）野外科研调查交通工具缺乏，表现在“交通靠（脚）走”的现象。因此，急需改善[NAME]的科研监测条件。`,
        backgroundParagraph3: `开展好[*]科学研究工作是[*]今后未来发展的方向，简单的科研设备难以做出高质量的科研成果。只有加强科研设备的投入，并开展和高校科研院所的合作，才能提升科研整体水平，更好地为当地经济发展服务。`
    },
    {
        key: '11',
        value: "2-3",
        problemType: '科研监测方面',
        problemDetails: "科研工作缺乏固定场所，科研水平有待进一步提高",
        checked: false,
        backgroundParagraph1: `[*]在建设过程中，科研工作以资源本底调查为主，研究领域单一，且以配合各科研院校、研究机构作一些辅助性协助工作为主，[*]难以独立开展科研。同时，由于缺乏必要的固定科研场所，监测设备落后，科研经费紧张等种种因素，科研工作难以向深层次广领域开展，在及时总结、交流、推广先进实用技术方面的工作欠缺，一定程度上影响了[*]建设和发展速度，难以适应国家和社会对[*]建设和发展的要求。`
    },
    {
        key: '12',
        value: "2-4",
        problemType: '科研监测方面',
        problemDetails: '调查监测和科学研究水平有待提升',
        checked: false,
        backgroundParagraph1: `由于[*]批复的财政资金投入有限，必备的科研监测设施建设和设备购置的数量有限，限制了[*]调查监测、科学研究、宣传教育等各项功能的正常发挥，也影响了保护管理能力的进一步提高。因此，[*]必须在继续加强保护管理基础设施建设的同时，加大科研监测投入。应规划围绕[*]主要保护对象，争取与科研单位、周边大学等科研院所，合作开展调查监测和研究项目。`
    },
    {
        key: '13',
        value: "2-5",
        problemType: '科研监测方面',
        problemDetails: '保护地需进一步开展资源调查和监测工作',
        checked: false,
        backgroundParagraph1: `对于生态系统类[*]而言，生态系统的监测是一项十分重要的工作。本底调查和定位监测可有效地了解生态系统及其保护对象数量分布和动态变化，从而有的放矢的实施保护措施，及时调整保护策略，提高保护能力。规划在[*]内开展资源调查工作，布设固定样方和样地，生态监测点位，摸清[*]野生动植物、空气、土壤、水环境情况，掌握[*]资源动态变化情况，积累基础研究资料。`
    },
    {
        key: '14',
        value: "3-1",
        problemType: '宣传教育方面',
        problemDetails: '宣传教育能力还需要提高',
        checked: false,
        backgroundParagraph1: `[NAME]建立以来，在[*]职工及周边群众开展了森林防火以及野生动植物保护方面的宣传。但是[NAME]宣教能力较低，体现在：（1）[*]没有用于宣教的宣教中心、宣教馆、宣教基地等，用于宣教的设备十分缺乏；（2）对外宣传较少，因为地处偏僻，社会外界对[*]了解少，缺乏对外宣传的渠道，如互联网平台等；（3）[*]工作人员学历偏低，知识更新缓慢，需要给予更多的培训机会；（4）[*]有关自然保护等方面宣传知识不足，宣传技能及对社区宣传的经验不够丰富。因此，急需提高罗山[*]宣教能力。`
    },
    {
        key: '15',
        value: "3-2",
        problemType: '宣传教育方面',
        problemDetails: '宣传教育的广度和深度还远远不够',
        checked: false,
        backgroundParagraph1: `[*]是开展自然环境保护、生物多样性保护及宣传教育的重要基地。在广度方面，[*]的宣教活动基本只对[*]参观者和周边的群众起到了较好的作用，对这区域基本靠宣传标牌、标语和巡护人员的口头宣传，宣传的力度和效果不甚理想。有待于进一步强化这项工作，加强社区群众对[*]意义和作用的认识，使他们主动、自觉、积极地支持、参与[*]的建设与管理，促进[*]由被动管护变为主动管护，提高保护管理成效。在深度方面，宣教设施功能不完善、手段方式简单、内容层次低，远远不能满足各类人群特别是大中专生及生态旅游爱好者的需要。
        [*]需要新建具有“[NAME]”地域特色，利用现代技术充分展示[*]丰富的物种、奇特的气候、能够产生巨大的社会效益的、集“科普、宣传、教育”于一体的多功能科教场馆，从而进一步提高公众保护自然资源意识。同时，通过加强与相关科研单位和大专院校的合作，为其提供教育实践和科研的基地，形成长期的科技支撑机制。`,
        backgroundParagraph2: `[*]位于[P]，道路交通便利，是宣传森林资源保护的重要窗口，近年来知名度越来越高，宣教工作任务也日益繁重，[*]现有宣教设施少，内容相对单一，规模偏小，宣教工作无法满足[*]在全国[*]建设体系中的定位需求，造成了自然资源和受众资源的双重浪费。规划在[*]充分挖掘[*]的宣传教育能力，改善宣传教育的软硬件环境，增强人们认识自然、保护自然的意识。`
    },
    {
        key: '16',
        value: "4-1",
        problemType: '生态旅游方面',
        problemDetails: '可持续发展能力需提高',
        checked: false,
        backgroundParagraph1: `[NAME]可持续发展能力急需提高，表现在：（1）[*]自养能力较差，护林员日常生活较艰苦；（2）生态旅游没有起步，自身造血功能较差，需发展生态旅游来加强[*]的自养能力。`
    },
    {
        key: '17',
        value: "5-1",
        problemType: '基础设施方面',
        problemDetails: '基础设施薄弱、配套设备缺乏',
        checked: false,
        backgroundParagraph1: `[*]基础设施建设和管理基本处于低水平状态。[*]区界标桩、各功能区区界标桩至今没有埋设，保护宣传等解说牌数量不足，[*]内生活设施条件简陋，道路级别低，缺乏必要的巡护交通工具，这严重地影响着[*]保护工作的正常开展。`,
        backgroundParagraph2: `[*]建立以来，在有限的资金投入下，虽已完成办公楼、管护站等必须的基础建设，但仍缺乏监测和巡护设备，基础设施、基本建设基本处于待建状态。`,
        backgroundParagraph3: `由于[*]资金来源相对不足，到目前为止，[*]仍缺少必要的管护、科研基础设施，而且有些设施简陋，远不能满足保护、管理与科研的需要。此外[*]境内至今没有国家电网，[*]边界、功能区基础设施的缺乏，影响了保护管理工作的有效实施，限制了[*]保护、监测、研究等各种功能的正常发挥。`,
        backgroundParagraph4: `管理站、护林点设施设备相对缺乏，体现在：（1）防火任务艰巨，没有专业的防火物资储备库；（2）护林点、检查站与哨卡，地处偏僻和远离城区，交通不便，不仅需解决职工住房，还要解决相应配套的储煤取暖房、水窖及家禽家畜养殖舍，以及改善护林点的卫生设施等；（3）办公条件相对不足，包括计算机、照相机、空调、打印机、办公桌、会议室家具等。因此，从基层留住人才角度出发，需要切实改善管理站、护林点基础设施与办公条件。`
    },
    {
        key: '18',
        value: "6-1",
        problemType: '防灾减灾方面',
        problemDetails: '火灾隐患较大，防火网络不够健全',
        checked: false,
        backgroundParagraph1: `[NAME]三面环山，沟壑纵横，断崖林立，地形复杂，枯枝落叶较多，秋冬季干旱少雨，火灾等级较高，加之区内交通不便，防火线没有完全贯通，因此，森林火灾隐患和防火压力大。`,
        backgroundParagraph2: `[*]周边人员密度大，村镇多，管护难度大，同时[NAME]地处重点公益林分布区和[*]群中，森林防火成为保护管理工作中的重中之重，[*]现有防火体系建设存在着以下问题：瞭望范围偏小；没有高标准的森林火灾扑救指挥系统，没有专用的防火物资仓库，现有装备落后；扑火队伍临时雇工多，流动性大，队伍不稳定。基于以上问题，很有必要提高火险测报工作科技含量，建设微波视频防火监测系统，配套监控中心等建设内容，为及时发现火灾隐患，降低火灾损失提供硬件条件。同时提高工作人员的视频监控系统的信息收集、使用和维护能力，以充分挖掘设备潜能，为及早发现、迅速出击提供保障。
        另外，林区防火道路通行能力较差，给森林火灾扑救带来很大的制约，因此，需对防火道路进行改善与整修，提高对突发火灾的及时处理能力。`,
        backgroundParagraph3: `森林火灾对生物多样性保护构成了最直接的威胁，也是最严重的威胁之一。[NAME]森林覆盖率较高，加上[*]特殊的自然环境形成的秋早春迟、冬季漫长，重点防火期长达半年之久。重点防火期内，气候多风干燥、植被含水量低、林下枯枝落叶多，火险程度高，很容易发生森林火灾。[*]火灾易发地区草坡、灌丛较多，并与村庄、农地接壤，给防火工作增加了难度；每年清明、冬至时节，当地村民传统的祭祀活动，加上社区群众森林防火意识还有待加强，威胁[*]的安全。[*]的防火设施落后，现代化高科技的预测预报系统不完善，山高、坡陡、谷深、起伏大等自然地理因素导致瞭望面积比例偏低，防火网络不够健全；视频监测系统还未实现全覆盖，灭火设施陈旧老化；再加上[*]山高坡陡，交通不便，通讯信号差，一旦发生火灾难以及时到达现场进行有效扑救，对森林资源产生威胁。
        为了加强[*]的护林防火工作，[*]应该一方面加强护林防火的宣传工作，增设防火警示牌，建立和完善森林火灾预测预报和监控系统，加强火源管理，严格用火审批制度，落实防火责任；另一方面，[*]与周边社区建立边界联防机制，形成联合巡护、联合值班、定期会议、联合执法等制度；同时，需要进一步提高森林消防队伍素质，增加消防设施投入等。`
    },
]


const getAZindex = (code: string): number => {
    let AZdict = [
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    ];
    let findIndex = -1;
    AZdict.forEach((char, index) => {
        if (char == code.toUpperCase()) {
            findIndex = index;
        }
    })
    return findIndex;
}

const getAZcode = (index: number): string => {
    let AZdict = [
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    ];
    return AZdict[index] ? AZdict[index] : 'UnKnow'
}


const getDPI = () => {
    let div = document.createElement("div");
    div.style.width = "1in";
    div.style.height = "1in";
    div.style.position = "absolute";
    div.style.left = "-100%";
    div.style.top = "-100%";
    document.body.appendChild(div);
    let dpi = div.offsetWidth;
    document.body.removeChild(div);
    return dpi;
}

const convertToPx = (value: number, unit: OfficeUnit) => {
    let dpi = getDPI();
    let px = 10;
    value = Number(value);
    if (isEmpty(unit)) {
        unit = 'lb';
    }
    switch (unit) {
        //英寸
        case 'inch':
            px = value * dpi;
            break;
        //厘米
        case 'cm':
            px = value * dpi / 2.54;
            break;
        //毫米
        case 'mm':
            px = value * dpi / 25.4;
            break;
        //磅
        case 'lb':
            px = value * dpi / 72;
            break;
        default:
            return null;
    }
    return Number(px.toFixed(2))
}

const convertPx2Lb = (value: number): number => {
    let dpi = getDPI();
    let lb = 10;
    value = Number(value);
    lb = value / dpi * 72;
    return Number(lb.toFixed(2));
}


const defaultDocExtraConfigInfo: DocExtraConfigInfo = {
    columnWidth: 72,
    rowHeight: 28,
    columnWidthUnit: 'lb',
    rowHeightUnit: 'lb',
    borderInfo: [],  //边框信息
    frozenInfo: {
        type: 'rangeRow',
        range: {
            row_focus: 3,
            column_focus: 0,
        }
    },
    zoom: 1,
    workSheetName: '投资估算表',
    cutsomColumnWidthMap: {
        '0': 136,
        '1': 320
    },
    cutsomRowHeightMap: {},
    scrollInfo: {
        scrollLeft: 0,
        scrollTop: 0,
    },
    speciesInfoList: [],
    hasRecommandMainProject: false,
    excelColumnFillMode: 'autoColumnWidth',
    hasOpenTopicManageModal: false
}

const handleJumpEditPage = (subLink: string) => {
    window.location.href = window.location.origin + subLink;
}


const convertSizeMapToPx = (sizeMap: any, sizeUnit: OfficeUnit) => {
    const { values, keys } = Object;
    let pxSizeMap = {};
    const indexList = keys(sizeMap);
    indexList.forEach((key, keyIndex) => {
        pxSizeMap[key] = convertToPx(sizeMap[key], sizeUnit);
    })
    return pxSizeMap;
}

const convertToHex = (color) => {
    let hexColor;
    // Check if color is RGB or RGBA
    if (color.startsWith('rgb')) {
        let colorsOnly = color.substring(color.indexOf('(') + 1, color.lastIndexOf(')')).split(/,\s*/);
        let r = parseInt(colorsOnly[0]);
        let g = parseInt(colorsOnly[1]);
        let b = parseInt(colorsOnly[2]);

        hexColor = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
    }
    // Check if color is HEX
    else if (color.startsWith('#')) {
        hexColor = color;
    }

    return hexColor;
}

const convertToPrimaryFF = (ff: string) => {
    let primaryFF = '宋体';
    switch (ff) {
        case '宋体（Song）':
            primaryFF = '宋体';
            break;
        case '微软雅黑':
            primaryFF = '微软雅黑';
            break;
        case '黑体（ST Heiti）':
            primaryFF = '黑体';
            break;
        case '仿宋（ST FangSong）':
            primaryFF = '仿宋';
            break;
        case 'Times New Roman':
            primaryFF = 'Times New Roman';
            break;
        default:
            break;
    }
    return primaryFF;
};

const addItemToUniquenList = (item: string, list: string[]) => {
    if (!list.includes(item)) {
        list.push(item)
    }
    return list;
}

const readWorkBookData = (result: any): TypeWorkBookReadResult[] => {
    const workBookData = [];
    const workbook = XLSX.read(result, { type: 'binary' });
    const workSheetNameList: string[] = workbook.SheetNames;
    workSheetNameList.forEach(workSheetName => {
        const sheet = workbook.Sheets[workSheetName]; // 获取第一个工作表
        const data = XLSX.utils.sheet_to_json(sheet);
        workBookData.push({
            sheetName: workSheetName,
            sheetData: data
        })
    })
    return workBookData;
}

const defaultProjectSubTableList: TypeProjectSubTable[] = [
    {
        workSheetData: [],
        workSheetFileName: "",
        type: "1",
        name: "社区情况统计表",
        fileName: "社区情况统计表上传模版.xlsx",
        url: "http://shpfile-data-1314977817.cos.ap-guangzhou.myqcloud.com/public/20230824543cca7621-c09d-462e-b2a7-b70226fb7d4384.xlsx"
    },
    {
        workSheetData: [],
        workSheetFileName: "",
        type: "2",
        name: "土地利用现状表",
        fileName: "土地利用现状表上传模版.xlsx",
        url: "http://cskk-admin.yunnanforestry.cn/cskk-export/public/uploads/file-1708654493580-669992863.xlsx"
    },
    {
        workSheetData: [],
        workSheetFileName: "",
        type: "3",
        name: "功能区划表",
        fileName: "功能区划表上传模版.xlsx",
        url: "http://shpfile-data-1314977817.cos.ap-guangzhou.myqcloud.com/public/2023082453bfc70327-34e4-4887-bad0-c51a6cd0a7b448.xlsx"
    },
    {
        workSheetData: [],
        workSheetFileName: "",
        type: "4",
        name: "人员现状统计表",
        fileName: "人员现状统计表上传模版.xlsx",
        url: "http://shpfile-data-1314977817.cos.ap-guangzhou.myqcloud.com/public/2023082411acc6ad25-ff0a-4dfc-b8c5-ecbad195449998.xlsx"
    },
    {
        workSheetData: [],
        workSheetFileName: "",
        type: "5",
        name: "基础设施现状统计表",
        fileName: "基础设施现状统计表上传模版.xlsx",
        url: "http://shpfile-data-1314977817.cos.ap-guangzhou.myqcloud.com/public/2023082430765a379d-1f4d-4ed5-bed7-c8f4428070eb17.xlsx"
    },
    {
        workSheetData: [],
        workSheetFileName: "",
        type: "6",
        name: "野生动植物资源统计表",
        fileName: "野生动植物资源统计表上传模版.xlsx",
        url: "http://shpfile-data-1314977817.cos.ap-guangzhou.myqcloud.com/public/20230824512668dc1a-5e2b-43d9-ae46-72343666d02e67.xlsx"
    }
]


const defaultDocBaseConfigInfo: DocConfigBaseInfo = {
    topicTreeLinkage: true,             //大纲联动
    showTopicTreeIcon: false,           //大纲图标
    topicSideBarLocked: false,          //大纲锁定
    showTopicTreeLevel: true,           //展示当前大纲的序号
    systemRecommandNodeDeleted: false,  //是否已删除系统推荐的大纲
    docMoneyCardinalNumber: 1,
    decimalNumber: 0.01,
    otherExpensesItemList: [
        {
            name: "勘查设计费用",
            value: 2.5
        },
        {
            name: "建设单位管理费",
            value: 2.0
        },
        {
            name: "工程监理费",
            value: 2.0
        },
        {
            name: "招标费",
            value: 0.5
        },
    ],
    basicReserveExpensesList: [
        {
            name: "基本预备费",
            value: 5
        },
    ]
};

const defaultColmunWidthConfigCollect: ColumnWidthConfigMap = {
    0: {
        columnIndex: 0,
        columnWidth: 160,
        columnWidthUnit: 'lb'
    },
    1: {
        columnIndex: 1,
        columnWidth: 320,
        columnWidthUnit: 'lb'
    }
}

const defaultExcelFormatBrushConfig: ExcelFormatBrush = {
    enable: false,
    type: 'single',
    tableDataCellList: [],
}

const proteclandTypeOnlineData = [
    {
        "label": "国家公园",
        "value": "6424f5e8019ebb24cd3d9e7c",
        "children": [
            {
                "label": "生态系统保护",
                "value": "6424f5e8019ebb24cd3d9e7c"
            },
            {
                "label": "野生动植物",
                "value": "6424f601019ebb24cd3d9e8c"
            },
            {
                "label": "自然景观",
                "value": "6424f60b019ebb24cd3d9e95"
            },
            {
                "label": "地质遗迹",
                "value": "6424f617019ebb24cd3d9e9e"
            }
        ]
    },
    {
        "label": "自然保护区",
        "value": "6424f622019ebb24cd3d9ea7",
        "children": [
            {
                "label": "森林生态系统",
                "value": "6424f622019ebb24cd3d9ea7"
            },
            {
                "label": "草原生态系统",
                "value": "6424f62d019ebb24cd3d9eb0"
            },
            {
                "label": "荒漠生态系统",
                "value": "6424f642019ebb24cd3d9eb9"
            },
            {
                "label": "湿地生态系统",
                "value": "6424f653019ebb24cd3d9ec2"
            },
            {
                "label": "海洋生态系统",
                "value": "6424f65d019ebb24cd3d9ecb"
            },
            {
                "label": "动植物保护类",
                "value": "6424f668019ebb24cd3d9ed4"
            },
            {
                "label": "地质遗迹类",
                "value": "6424f672019ebb24cd3d9edd"
            },
            {
                "label": "古生物遗迹类",
                "value": "6424f680019ebb24cd3d9ee6"
            }
        ]
    },
    {
        "label": "自然公园",
        "value": "6424f68c019ebb24cd3d9eef",
        "children": [
            {
                "label": "森林公园",
                "value": "6424f68c019ebb24cd3d9eef"
            },
            {
                "label": "地质公园",
                "value": "6424f697019ebb24cd3d9ef8"
            },
            {
                "label": "湿地公园",
                "value": "6424f6a5019ebb24cd3d9f01"
            },
            {
                "label": "草原公园",
                "value": "6424f6c0019ebb24cd3d9f0e"
            },
            {
                "label": "海洋公园",
                "value": "6424f70f019ebb24cd3d9f1a"
            },
            {
                "label": "沙漠公园",
                "value": "6424f725019ebb24cd3d9f23"
            },
            {
                "label": "风景名胜区",
                "value": "6424f737019ebb24cd3d9f2c"
            }
        ]
    }
]

// function deepCloneV2(data, hash = new WeakMap()) {
//     if(isEmpty(data)){
//         return data;
//     }
//     if(isArray(data)){
//         let newList = [];
//         data.forEach(item => {
//             newList.push(deepCloneV2(item));
//         })
//         return newList;
//     }
//     if(typeof data !== 'object' || data === null){
//         console.log("data--->", data)
//           throw new TypeError('传入参数不是对象')
//       }
//     // 判断传入的待拷贝对象的引用是否存在于hash中
//     if(hash.has(data)) {
//           return hash.get(data)
//       }
//     let newData = {};
//     const dataKeys = Object.keys(data);
//     dataKeys.forEach(value => {
//        const currentDataValue = data[value];
//        // 基本数据类型的值和函数直接赋值拷贝 
//        if (typeof currentDataValue !== "object" || currentDataValue === null) {
//             newData[value] = currentDataValue;
//         } else if (Array.isArray(currentDataValue)) {
//            // 实现数组的深拷贝
//           newData[value] = [...currentDataValue];
//         } else if (currentDataValue instanceof Set) {
//            // 实现set数据的深拷贝
//            //@ts-ignore
//            newData[value] = new Set([...currentDataValue]);
//         } else if (currentDataValue instanceof Map) {
//            // 实现map数据的深拷贝
//             //@ts-ignore
//            newData[value] = new Map([...currentDataValue]);
//         } else { 
//            // 将这个待拷贝对象的引用存于hash中
//            hash.set(data,data)
//            // 普通对象则递归赋值
//             //@ts-ignore
//            newData[value] = deepCopy(currentDataValue, hash);
//         } 
//      }); 
//     return newData;
// }

function deepCloneV2(target) {
    if (isEmpty(target)) {
        return target;
    }
    return JSON.parse(JSON.stringify(target));
    if (isArray(target)) {
        let [...newList] = target;
        console.log("newList--->", newList)
        return newList;
    }
    return { ...target }

    // if (isArray(target)) {
    //     let newList = [];
    //     target.forEach(item => {
    //         // console.log("111", deepCloneV2(item))
    //         newList.push(deepCloneV2(item));
    //     })
    //     return newList;
    // }

    // WeakMap作为记录对象Hash表（用于防止循环引用）
    const map = new WeakMap()

    // 判断是否为object类型的辅助函数，减少重复代码
    function isObject(target) {
        return (typeof target === 'object' && target) || typeof target === 'function'
    }

    function clone(data) {

        // 基础类型直接返回值
        if (!isObject(data)) {
            return data
        }

        // 日期或者正则对象则直接构造一个新的对象返回
        if ([Date, RegExp].includes(data.constructor)) {
            return new data.constructor(data)
        }

        // 处理函数对象
        if (typeof data === 'function') {
            return new Function('return ' + data.toString())()
        }

        // 如果该对象已存在，则直接返回该对象
        const exist = map.get(data)
        if (exist) {
            return exist
        }

        // 处理Map对象
        if (data instanceof Map) {
            const result = new Map()
            map.set(data, result)
            data.forEach((val, key) => {
                // 注意：map中的值为object的话也得深拷贝
                if (isObject(val)) {
                    result.set(key, clone(val))
                } else {
                    result.set(key, val)
                }
            })
            return result
        }

        // 处理Set对象
        if (data instanceof Set) {
            const result = new Set()
            map.set(data, result)
            data.forEach(val => {
                // 注意：set中的值为object的话也得深拷贝
                if (isObject(val)) {
                    result.add(clone(val))
                } else {
                    result.add(val)
                }
            })
            return result
        }

        // 收集键名（考虑了以Symbol作为key以及不可枚举的属性）
        const keys = Reflect.ownKeys(data)
        // 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述
        const allDesc = Object.getOwnPropertyDescriptors(data)
        // 结合 Object 的 create 方法创建一个新对象，并继承传入原对象的原型链， 这里得到的result是对data的浅拷贝
        const result = Object.create(Object.getPrototypeOf(data), allDesc)

        // 新对象加入到map中，进行记录
        map.set(data, result)

        // Object.create()是浅拷贝，所以要判断并递归执行深拷贝
        keys.forEach(key => {
            const val = data[key]
            if (isObject(val)) {
                // 属性值为 对象类型 或 函数对象 的话也需要进行深拷贝
                result[key] = clone(val)
            } else {
                result[key] = val
            }
        })
        return result
    }

    return clone(target)
}

function deleteNodeAndMoveChildren(treeData, deleteNodeId) {
    // 递归查找并处理指定节点
    const findAndDelete = (nodes, parentId) => {
        return nodes.reduce((acc, node) => {
            // 如果当前节点是要删除的节点
            if (node.id === deleteNodeId) {
                // 如果存在子节点，将这些子节点添加到父节点的children中
                if (node.children) {
                    node.children.forEach(child => {
                        // 更新子节点的pid为被删除节点的父节点ID
                        child.pid = parentId;
                        acc.push(child);
                    });
                }
                // 跳过当前节点，不将其添加到累加器中
                return acc;
            }

            // 如果当前节点不是要删除的节点，但有子节点，递归处理子节点
            if (node.children) {
                node.children = findAndDelete(node.children, node.id);
            }

            // 将当前节点添加到累加器中
            acc.push(node);
            return acc;
        }, []);
    };

    // 从树的顶层开始处理
    return findAndDelete(treeData, null);
}


const findTargetIndexInArrayListById = (tagrtId: string | number, arrayList: any[]): number => {
    let tagrtIndex = -1;
    for (let i = 0; i < arrayList.length; i++) {
        if (arrayList[i].id == tagrtId) {
            tagrtIndex = i;
            break;
        }
    }
    return tagrtIndex;
};

const checkIsTempAccount = () => {
    try {
        const userInfo = redux.getState().systemConfig.userInfo;
        // const userInfo = JSON.parse(localStorage.getItem("systemConfig"))['userInfo'];
        return userInfo && userInfo.userType == 'temp';
    } catch (e) {
        return false;
    }
}

const getNodeChildDeviceNodeCount = (node: TopicType): number => {
    let deviceNodeCount = 0;
    if (node.children) {
        node.children.forEach(subNode => {
            if (subNode.topicType == 'device') {
                deviceNodeCount++;
            } else {
                const subNodeDeviceNodeTotalCount = getNodeChildDeviceNodeCount(subNode);
                deviceNodeCount += subNodeDeviceNodeTotalCount;
            }
        })
    }
    return deviceNodeCount;
};

const getNodeChildDeviceNodeCheckedCount = (node: TopicType): number => {
    let deviceNodeCount = 0;
    if (node.children) {
        node.children.forEach(subNode => {
            if (subNode.topicType == 'device') {
                if (subNode.checked) {
                    deviceNodeCount++;
                }
            } else {
                const subNodeDeviceNodeTotalCount = getNodeChildDeviceNodeCheckedCount(subNode);
                deviceNodeCount += subNodeDeviceNodeTotalCount;
            }
        })
    }
    return deviceNodeCount;
};


const insertChildNodeListToTree = (tree: TopicType[], list: TopicType[], targetNodeId: string) => {
    tree.forEach(node => {
        if (node.id == targetNodeId) {
            list && list.length && list.forEach(item => {
                item.pid = targetNodeId;
            })
            if (node.children && node.children.length) {
                node.children = node.children.concat(list);
            } else {
                node.children = list;
            }
        } else if (node.children) {
            insertChildNodeListToTree(node.children, list, targetNodeId)
        }
    })
}

const insertTopPeerNodeListToTree = (tree: TopicType[], list: TopicType[], targetNodeId: string) => {
    let findTopTagetNodeIndex = -1;
    tree.forEach((node, nodeIndex) => {
        if (node.id == targetNodeId) {
            findTopTagetNodeIndex = nodeIndex;
        }
    })
    list.forEach(item => {
        item.pid = '0';
    })
    let tempTreeData = tree;
    if (findTopTagetNodeIndex > -1) {
        const lastArr = tempTreeData.splice(findTopTagetNodeIndex + 1, tree.length - 1);
        tempTreeData = tempTreeData.concat(list).concat(lastArr)
    }
    return tempTreeData;
}

const insertPeerNodeListToTree = (tree: TopicType[], list: TopicType[], targetNodeId: string) => {
    list.forEach(topic => {
        topic.wordParagraphList = [];
    })
    tree.forEach((node, index, array) => {
        if (node.children && node.children.length) {
            let targetIndex = node.children.findIndex(subNode => subNode.id === targetNodeId);
            if (targetIndex !== -1) {
                list.forEach(item => {
                    item.pid = node.id;
                });
                node.children.splice(targetIndex + 1, 0, ...list);
            } else {
                insertPeerNodeListToTree(node.children, list, targetNodeId);
            }
        }
    });
};

const arrayTreeFilter = (data, predicate, filterText) => {
    const nodes = data;
    if (!(nodes && nodes.length)) {
        return;
    }
    const newChildren = [];
    for (const node of nodes) {
        if (predicate(node, filterText)) {
            // 如果自己（节点）符合条件，直接加入到新的节点集
            newChildren.push(node);
            // 并接着处理其 children,（因为父节点符合，子节点一定要在，所以这一步就不递归了）
            node.children = arrayTreeFilter(node.children, predicate, filterText);
        } else {
            // 如果自己不符合条件，需要根据子集来判断它是否将其加入新节点集
            // 根据递归调用 arrayTreeFilter() 的返回值来判断
            const subs = arrayTreeFilter(node.children, predicate, filterText);
            // 以下两个条件任何一个成立，当前节点都应该加入到新子节点集中
            // 1. 子孙节点中存在符合条件的，即 subs 数组中有值
            // 2. 自己本身符合条件
            if ((subs && subs.length) || predicate(node, filterText)) {
                node.children = subs;
                newChildren.push(node);
            }
        }
    }
    return newChildren;
}

const expandedKeysFun = (treeData) => {
    if (treeData && treeData.length == 0) {
        return [];
    }
    let arr = [];
    const expandedKeysFn = (treeData) => {
        treeData.map((item, index) => {
            arr.push(item.key);
            if (item.children && item.children.length > 0) {
                expandedKeysFn(item.children);
            }
        })
    }
    expandedKeysFn(treeData);
    return arr;
}

const getTopicMaxBackgroundIndex = (topic: TopicType) => {
    const backgroundKeyList = Object.keys(topic).filter(key => key.includes('backgroundParagraph'));
    let topicMaxBackgroundIndex = 1;
    backgroundKeyList.forEach(key => {
        const index = Number(key.split('backgroundParagraph')[1]);
        if (index > topicMaxBackgroundIndex) {
            if (!isEmpty(topic[key])) {
                topicMaxBackgroundIndex = index;
            }
        }
    })
    return topicMaxBackgroundIndex;
}

function findTreePathByNodeId(forest, targetId) {
    // 遍历每一个根节点
    for (let root of forest) {
        // 调用递归函数搜索路径
        const path = findPath(root, targetId);
        // 如果找到路径（非空数组），返回这个路径
        if (path.length) {
            return path;
        }
    }
    // 如果所有根节点都没有找到路径，返回空数组
    return [];
}

function findPath(root, targetId) {
    if (root.id === targetId) {
        return [root.id];
    }
    if (root.children) {
        for (let child of root.children) {
            const path = findPath(child, targetId);
            if (path.length) {
                return [root.id, ...path];
            }
        }
    }
    return [];
}


export {
    findTreePathByNodeId,
    getTopicMaxBackgroundIndex,
    arrayTreeFilter,
    expandedKeysFun,
    insertChildNodeListToTree,
    insertTopPeerNodeListToTree,
    insertPeerNodeListToTree,
    getNodeChildDeviceNodeCount,
    getNodeChildDeviceNodeCheckedCount,
    checkIsTempAccount,
    makeClassNameList,
    colorRGB2Hex,
    hexToRgba,
    deepCopy,
    adaptFontFamily,
    adaptFontSize2Px,
    fontSizeTemplateList,
    deepClone,
    _useDebounce,
    useDebounce,
    getChangeEventValue,
    toastShort,
    isEmpty,
    commonErrorMsg,
    commonSuccessMsg,
    addKeyToList,
    formatTime,
    formatYear,
    generateTreeData,
    defaultAvatar,
    getUuid,
    adaptTopicLevel,
    provinceList,
    protectlandProblemList,
    generateOptionData,
    addTreePropertyForList,
    tree2List,
    addTopicIdForTopicList,
    filterTree,
    filterTreeByDeviceNode,
    checkIsDeviceTopic,
    dfsRecursive,
    parsePrice,
    hasCommonPrefix,
    dfsRecursiveFromInner,
    findHalfCheckedKeys,
    inlineStyleTemplateList,
    getCheckBoxChangeEventValue,
    filterTopicListToNetServe,
    tableCellDefaultBlockStyle,
    comparecomDocInstance,
    findNodesWithChildren,
    findTreeNode,
    swapListElements,
    findLastTreeNode,
    fontSizeList,
    fontFamilyList,
    convertStyleMapToObject,
    copyTextToClipboard,
    isMacOS,
    ctrlKeyName,
    findExcelStyleByCell,
    mergeCellConfigList,
    convertCellStyleConfig,
    defaultExcelCellStyleConfig,
    getObjectWithIntersectionAttributes,
    defaultDocConfig,
    deleteObjectEmptyKey,
    adaptDocMoneyCardinalNumberName,
    handleDownloadByName,
    getTextWidth,
    checkTwoDocInstanceEquals,
    getCurrentDateNs,
    getSingleTdTableHeight,
    insertArrayAtPosition,
    isNonNegativeNumeric,
    getFileType,
    getFileTypeName,
    getFileSize,
    findFolderPath,
    replaceUnitUpperCase,
    protectlandProblemTableData,
    getAZindex,
    getAZcode,
    defaultDocExtraConfigInfo,
    convertToPx,
    handleJumpEditPage,
    convertSizeMapToPx,
    convertToHex,
    convertToPrimaryFF,
    addItemToUniquenList,
    readWorkBookData,
    downloadAndRenameFile,
    isNumber,
    defaultProjectSubTableList,
    defaultDocBaseConfigInfo,
    convertPx2Lb,
    defaultColmunWidthConfigCollect,
    defaultExcelFormatBrushConfig,
    protectlandLevelList,
    proteclandTypeOnlineData,
    deepCloneV2,
    deleteNodeAndMoveChildren,
    findTargetIndexInArrayListById,
    handleDownloadByNameV2
}